/**
 * @Copyright:Copyright (c) 2007 - 2023
 * 
 */
package com.tengtong.duang.app.query.core;

import java.util.LinkedList;
import java.util.Map;

//import com.tengtong.commons.base.KeyValue;
import com.tengtong.commons.collections.CollectionUtils;
import com.tengtong.commons.collections.ListUtils;
import com.tengtong.commons.collections.MapUtils;
import com.tengtong.commons.json.Json;
import com.tengtong.commons.json.Jsons;
import com.tengtong.commons.lang.ArrayUtils;
import com.tengtong.commons.lang.ExceptionUtils;
import com.tengtong.commons.lang.NumberUtils;
import com.tengtong.commons.lang.StringBuilderX;
import com.tengtong.commons.lang.StringUtils;
import com.tengtong.db.lang.QuerySortClass.Sort;
import com.tengtong.duang.app.query.core.QueryConstants.BooleanOp;
import com.tengtong.duang.sql.utils.SqlUtils;

/**
 * 查询语句的分析
 *
 * @author lin_jinming
 * @since: v1.0
 */
public class QueryStatementParser {
    
    private ColumnsBuilder columnsBuilder;
    
    private ConditionExpressParser expressParser;
    
    // 数量限制
    private int limit = -1;
    
    // 排序
    private String orderBy;
    
    // groupby
    private String groupBy;
    
    public QueryStatementParser() {
        this.expressParser = new ConditionExpressParser();
    }

    /**
     * 分析条件，得到一个对象栈
     *
     * @param conditions
     * @return
     */
    public LinkedList<Object> parse(Map<String, Object> conditions){
        LinkedList<Object> datas = new LinkedList<>();
        doIt(datas, conditions, null);
        
        return datas;
    }
    
    /**
     * datas 内包含以下几种元素： <br>
     * 
     *  1)单个表达式 KeyValue 对象；
     *  2)操作符
     *  3)子查询表达式 LinkedList
     *
     * @param datas       结果列表
     * @param conditions  表达式表
     * @param op
     */
    private void doIt(LinkedList<Object> datas, Map<String, Object> conditions, BooleanOp op) {
        if(conditions != null && !conditions.isEmpty()) {
            conditions.forEach((name, o)->{
                var nameStr = name;
                if(name.charAt(0)  == '@') {
                    nameStr = name.substring(1);
                    switch(nameStr) {
                        case "columns" -> { // 选择字段 （TODO 只能在表下使用，不能用于条件内部）
                            // 字段
                            if(op != null) {
                                throw ExpressParseException.of("语法错误|条件内不能使用 @columns");
                            }
                            
                            parseColumns(o);                            
                        }
                        case "limit"   -> { // 记录数据限制
                            parseLimit(o);
                        }
                        case "orderBy" -> { // 排序
                            parseOrderBy(o);
                        }
                        case "groupBy" -> {
                            parseGroupBy(o);
                        }
                        default  -> {
                            //parseConditionExpress(datas, op, o, nameStr);
                            expressParser.parseConditionExpress(datas, op, nameStr, o);
                        }
                    }
                } else {
                    //addExpress(datas, op, name, o);
                    expressParser.putFieldValueExpress(datas, op, name, o);
                }                
            });
        }
    }

    /**
     * 将单个的关系表达式放入队列
     *
     * @param datas
     * @param op
     * @param name
     * @param o
     */
    private void addExpress(LinkedList<Object> datas, BooleanOp op, String name, Object o) {
        FieldValueExpress keyValue = FieldValueExpress.of(name, o);
        if(!datas.isEmpty()) {
            if(!BooleanOp.check(datas.peek())) {
                // 栈顶是数据，就要压一个操作入栈
                if(op != null) {
                    datas.push(op); // 压操作符入栈
                } else {
                    datas.push(BooleanOp.AND);
                    // 语法错误，即都有数据，但没有操作
                    // throw ExceptionUtils.ofRuntime("语法错误|都有数据没操作");
                }
            }
        }
        datas.push(keyValue);
    }

    /**
     * 对条件表达式进行分析
     *
     * @param datas 传入与传出结果
     * @param op    上一级的布尔操作
     * @param o     条件表达式
     * @param nameStr 本级的布尔操作？
     */
    private void parseConditionExpress(LinkedList<Object> datas, BooleanOp op, Object o, String nameStr) {
        var newOp = BooleanOp.fromValue(nameStr);
        if(newOp != null){
            if(newOp == BooleanOp.NOT){
                if(o instanceof String fields){ // 字符串类型时，使用逗号分隔成多个字段
                    datas.push(newOp);
                    datas.push(fields.split(","));
                } else if(o instanceof Json || o instanceof Map) {
                    var tmps = ListUtils.newLinkedList();
                    doIt(tmps, MapUtils.asLinkedMap(o), newOp);
                    if(CollectionUtils.notEmpty(tmps)) {
                        datas.push(newOp);
                        datas.push(tmps);
                    }                    
                }
            } else {
                // name 为一个布尔量，而 o 一定是一个对象
                if(o instanceof Json || o instanceof Map) {
                    var tmps = ListUtils.newLinkedList();
                    doIt(tmps, MapUtils.asLinkedMap(o), newOp);
                    if(CollectionUtils.notEmpty(tmps)) {
                        if(datas.peek() != null) {
                            datas.push(op == null ? BooleanOp.AND:op);
                        }
                        datas.push(tmps);
                    }
                } else {
                    if(o instanceof Jsons js) { // 条件数组
                        for(var json: js) {
                            var tmps = ListUtils.newLinkedList();
                            doIt(tmps, json.toMap(), newOp);
                            if(CollectionUtils.notEmpty(tmps)) {
                                if(datas.peek() != null) {
                                    datas.push(newOp);
                                }
                                datas.push(tmps);
                            }
                        }
                    } else {                        
                        throw ExceptionUtils.ofRuntime("语法错误|布尔表达式的值应该是对象");
                    }
                }

            }
        } else {
            throw ExceptionUtils.ofRuntime("语法错误|不支持的布尔操作:%s", newOp);  
        }
    }

    /**
     * 分析处理 @columns 的内容（所选字段）
     *
     * @param o
     */
    private void parseColumns(Object o) {
        if(o instanceof String cs) {
            if(StringUtils.notEmpty(cs)) {
                getColumnsBuilder().addColumns(StringUtils.splitByComma(cs));
            } else {
                throw ExceptionUtils.ofRuntime("语法错误|@columns 不能为空"); 
            }
            
        } else {
            if(o.getClass().isArray()) {
                // 逐个加入
                ArrayUtils.eachArray(o, 
                        (f)->getColumnsBuilder().addColumn(StringUtils.tryToString(f, "")));
            } else {
                throw ExceptionUtils.ofRuntime("语法错误|@columns 值类型只能是字符串或字符串数组"); 
            }
        }
    }
    
    /**
     * 记录数量
     *
     * @param o 大于 0 的整数
     */
    private void parseLimit(Object o){
        try {
            this.limit = NumberUtils.toInt(o, ExceptionUtils.ofRuntime("语法错误|@limit 的值只能是数值类型"));
        }catch (Throwable e) {
            throw ExceptionUtils.ofOrElse(e, "语法错误|@limit 的值只能是数值类型");
        }
    }
    
    /**
     * 排序，格式语法使用 sql 的: <fieldName> [asc|desc] ，如： name asc, age desc  
     *
     * @param o
     */
    private void parseOrderBy(Object o) {
        if(o instanceof String by) {
            if(SqlUtils.checkOrderBy(by)) {
                this.orderBy = by;
            } else {
                throw ExceptionUtils.ofRuntime("语法错误|@orderBy 语法错误");
            }
        } else if(o instanceof Json || o instanceof Map) {
            StringBuilderX sb = new StringBuilderX();
            MapUtils.asLinkedMap(o)
                    .forEach((f, d)->{
                        if(sb.length() > 0) {
                            sb.append(",");
                        }
                        sb.append(Sort.of(f, StringUtils.asString(o)));
                    });
            this.orderBy = sb.toString();
        } else {
            throw ExceptionUtils.ofRuntime("语法错误|@orderBy 语法错误");
        }
    }
    
    private void parseGroupBy(Object o) {
        if(o == null) {
            throw ExceptionUtils.ofRuntime("语法错误|@groupBy 不能为空");
        }
        
        if(o instanceof String g) {
            if(SqlUtils.checkGroupBy(g)) {
                this.groupBy = g;
            } else {
                throw ExceptionUtils.ofRuntime("语法错误|@groupBy 语法错误");
            }
        } else {
            throw ExceptionUtils.ofRuntime("语法错误|@groupBy 语法错误");
        }
    }
    
    public ColumnsBuilder getColumnsBuilder() {
        if(this.columnsBuilder == null) {
            this.columnsBuilder = new ColumnsBuilder();
        }
        return this.columnsBuilder;
    }
    
    public String getOrderBy() {
        return this.orderBy;
    }
    
    public String getGroupBy() {
        return this.groupBy;
    }
    
    /**
     * 数量
     *
     * @return
     */
    public int getLimit() {
        return this.limit;
    }
}
